#include "sdk.h"
#include "compilercommandgenerator.h"
#include <wx/intl.h>
#include <wx/filename.h>
#include "compilerfactory.h"
#include "compiler.h"
#include "manager.h"
#include "configmanager.h"
#include "logmanager.h"
#include "macrosmanager.h"
#include "scriptingmanager.h"
#include "filefilters.h"
#include "scripting/bindings/sc_base_types.h"
#include "scripting/sqplus/sqplus.h"

inline wxString UnquoteStringIfNeeded( const wxString& str ) {
  wxString s = str;
  if( !str.IsEmpty() && str.GetChar( 0 ) == _T( '"' ) && str.Last() == _T( '"' ) ) {
    s = str.Mid( 1, str.Length() - 2 );
  }
  return s;
}

CompilerCommandGenerator::CompilerCommandGenerator() {
}

CompilerCommandGenerator::~CompilerCommandGenerator() {
}

void CompilerCommandGenerator::Init( M_Code_Project* project ) {
  m_Output.clear();
  m_StaticOutput.clear();
  m_DefOutput.clear();
  m_Inc.clear();
  m_Lib.clear();
  m_CFlags.clear();
  m_LDFlags.clear();
  m_LDAdd.clear();
  m_CompilerSearchDirs.clear();
  m_LinkerSearchDirs.clear();
  Compiler* compiler = CompilerFactory::GetDefaultCompiler();
  if( !compiler ) {
    m_Throw( _T( "Default compiler is invalid!" ) );
  }
  if( !project ) {
    m_DefOutput[nullptr] = SetupOutputFilenames( compiler, nullptr );
    m_Inc[nullptr] = SetupIncludeDirs( compiler, nullptr );
    m_Lib[nullptr] = SetupLibrariesDirs( compiler, nullptr );
    m_CFlags[nullptr] = SetupCompilerOptions( compiler, nullptr );
    m_LDFlags[nullptr] = SetupLinkerOptions( compiler, nullptr );
    m_LDAdd[nullptr] = SetupLinkLibraries( compiler, nullptr );
    return;
  } else {
    m_PrjIncPath = project->GetCommonTopLevelPath();
    if( !m_PrjIncPath.IsEmpty() ) {
      if( m_PrjIncPath.Last() == _T( '\\' ) ) {
        m_PrjIncPath.RemoveLast();
      }
      QuoteStringIfNeeded( m_PrjIncPath );
    }
  }
  m_NotLoadedScripts.Clear();
  m_ScriptsWithErrors.Clear();
  wxSetWorkingDirectory( project->GetBasePath() );
  bool projectWasModified = project->GetModified();
  CompileTargetBase backup = *( CompileTargetBase* )project;
  CodeBlocksEvent evt( cbEVT_COMPILER_SET_BUILD_OPTIONS, 0, project );
  Manager::Get()->ProcessEvent( evt );
  DoBuildScripts( project, project, _T( "SetBuildOptions" ) );
  for( int i = 0; i < project->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget* target = project->GetBuildTarget( i );
    compiler = CompilerFactory::GetCompiler( target->GetCompilerID() );
    if( !compiler || target->GetTargetType() == ttCommandsOnly || false ) {
      m_Output[target] = wxEmptyString;
      m_StaticOutput[target] = wxEmptyString;
      m_DefOutput[target] = wxEmptyString;
      m_Inc[target] = wxEmptyString;
      m_Lib[target] = wxEmptyString;
      m_CFlags[target] = wxEmptyString;
      m_LDFlags[target] = wxEmptyString;
      m_LDAdd[target] = wxEmptyString;
      continue;
    }
    CompileTargetBase backuptarget = *( CompileTargetBase* )target;
    CodeBlocksEvent evt( cbEVT_COMPILER_SET_BUILD_OPTIONS, 0, project );
    evt.SetBuildTargetName( target->GetTitle() );
    Manager::Get()->ProcessEvent( evt );
    DoBuildScripts( project, target, _T( "SetBuildOptions" ) );
    m_DefOutput[target] = SetupOutputFilenames( compiler, target );
    m_Inc[target] = SetupIncludeDirs( compiler, target );
    m_Lib[target] = SetupLibrariesDirs( compiler, target );
    m_CFlags[target] = SetupCompilerOptions( compiler, target );
    m_LDFlags[target] = SetupLinkerOptions( compiler, target );
    m_LDAdd[target] = SetupLinkLibraries( compiler, target );
    *( CompileTargetBase* )target = backuptarget;
  }
  *( CompileTargetBase* )project = backup;
  project->SetModified( projectWasModified );
  if( !m_NotLoadedScripts.IsEmpty() || !m_ScriptsWithErrors.IsEmpty() ) {
    wxString msg;
    if( !m_NotLoadedScripts.IsEmpty() ) {
      msg << _T( "Scripts that failed to load either because they don't exist\n"
                 "or because they contain syntax errors:\n\n" );
      for( size_t i = 0; i < m_NotLoadedScripts.GetCount(); ++i ) {
        msg << m_NotLoadedScripts[i] << _T( "\n" );
      }
      msg << _T( "\n" );
    }
    if( !m_ScriptsWithErrors.IsEmpty() ) {
      msg << _T( "Scripts that failed to load because the mandatory function\n"
                 "SetBuildOptions() is missing:\n\n" );
      for( size_t i = 0; i < m_ScriptsWithErrors.GetCount(); ++i ) {
        msg << m_ScriptsWithErrors[i] << _T( "\n" );
      }
      msg << _T( "\n" );
    }
    if( Manager::IsBatchBuild() ) {
      Manager::Get()->GetLogManager()->LogInf( msg );
    } else
    { msging( msg, _T( "错误" ), wxICON_ERROR ); }
  }
}

void CompilerCommandGenerator::GenerateCommandLine( wxString& macro, ProjectBuildTarget* target, ProjectFile* pf, const wxString& file, const wxString& object,
    const wxString& flat_object, const wxString& deps ) {
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : CompilerFactory::GetDefaultCompiler();
  if( !compiler ) {
    Manager::Get()->GetLogManager()->LogInf( _T( "丢失编译器" ) );
    macro.Clear();
    return;
  }
  enum CompilerExe {
    ceUnknown,
    ceC,
    ceCPP
  };
  CompilerExe compExec = ceUnknown;
  wxString compilerStr;
  if( pf ) {
    if( pf->compilerVar.Matches( _T( "CPP" ) ) ) {
      compilerStr = compiler->GetPrograms().CPP;
      compExec = ceCPP;
    } else if( pf->compilerVar.Matches( _T( "CC" ) ) ) {
      compilerStr = compiler->GetPrograms().C;
      compExec = ceC;
    }
  } else {
    wxFileName fname( UnquoteStringIfNeeded( file ) );
    if( fname.GetExt().Lower().Matches( _T( "c" ) ) ) {
      compilerStr = compiler->GetPrograms().C;
      compExec = ceC;
    } else {
      compilerStr = compiler->GetPrograms().CPP;
      compExec = ceCPP;
    }
  }
  if( ( compilerStr.IsEmpty() && macro.Contains( _T( "$compiler" ) ) ) || ( compiler->GetPrograms().LD.IsEmpty() && macro.Contains( _T( "$linker" ) ) )
      || ( compiler->GetPrograms().LIB.IsEmpty() && macro.Contains( _T( "$lib_linker" ) ) ) ) {
    Manager::Get()->GetLogManager()->LogInf( F( _T( "GenerateCommandLine: Required compiler executable (%s) not found! Check the toolchain settings." ), file.wx_str() ) );
    macro.Clear();
    return;
  }
  FixPathSeparators( compiler, compilerStr );
  wxString tmpIncludes( m_Inc[target] );
  if( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_file_cwd" ), false ) ) {
    wxFileName fileCwd( UnquoteStringIfNeeded( file ) );
    wxString fileInc = fileCwd.GetPath();
    FixPathSeparators( compiler, fileInc );
    if( !fileInc.IsEmpty() ) {
      QuoteStringIfNeeded( fileInc );
      if( compiler->GetSwitches().includeDirs.EndsWith( _T( "(" ) ) ) {
        tmpIncludes.RemoveLast();
        tmpIncludes += compiler->GetSwitches().includeDirSeparator + fileInc + _T( ")" );
      } else {
        tmpIncludes += compiler->GetSwitches().includeDirSeparator + compiler->GetSwitches().includeDirs + fileInc;
      }
    }
  }
  if( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_prj_cwd" ), false ) ) {
    wxString fileInc = m_PrjIncPath;
    FixPathSeparators( compiler, fileInc );
    if( compiler->GetSwitches().includeDirs.EndsWith( _T( "(" ) ) ) {
      tmpIncludes.RemoveLast();
      tmpIncludes += compiler->GetSwitches().includeDirSeparator + fileInc + _T( ")" );
    } else {
      tmpIncludes += compiler->GetSwitches().includeDirSeparator + compiler->GetSwitches().includeDirs + fileInc;
    }
  }
  wxString tmp;
  wxString tmpFile = file;
  wxString tmpDeps = deps;
  wxString tmpObject = object;
  wxString tmpFlatObject = flat_object;
  wxFileName tmpFname( UnquoteStringIfNeeded( tmpFile ) );
  wxFileName tmpOutFname;
  FixPathSeparators( compiler, tmpFile );
  FixPathSeparators( compiler, tmpDeps );
  FixPathSeparators( compiler, tmpObject );
  FixPathSeparators( compiler, tmpFlatObject );
  wxString cFlags = m_CFlags[target];
  wxArrayString remFlags;
  if( compExec == ceC ) {
    remFlags = GetArrayFromString( compiler->GetCPPOnlyFlags(), _T( " " ) );
  } else if( compExec == ceCPP ) {
    remFlags = GetArrayFromString( compiler->GetCOnlyFlags(), _T( " " ) );
  }
  if( !remFlags.IsEmpty() ) {
    wxArrayString aCflags = GetArrayFromString( cFlags, _T( " " ) );
    for( size_t i = 0; i < remFlags.GetCount(); ++i ) {
      int index = aCflags.Index( remFlags[i] );
      if( index != wxNOT_FOUND ) {
        aCflags.RemoveAt( index );
      }
    }
    cFlags = GetStringFromArray( aCflags, _T( " " ), false );
  }
  wxString allObjectsQuoted( tmpObject );
  if( !( allObjectsQuoted.IsEmpty() || m_LDAdd[target].IsEmpty() ) ) {
    allObjectsQuoted += compiler->GetSwitches().objectSeparator;
  }
  allObjectsQuoted += m_LDAdd[target];
  if( allObjectsQuoted.Find( _T( '"' ) ) != -1 ) {
    allObjectsQuoted.Replace( _T( "\"" ), _T( "\\\"" ) );
    allObjectsQuoted = _T( "\"" ) + allObjectsQuoted + _T( "\"" );
  }
  macro.Replace( _T( "$compiler" ), compilerStr );
  macro.Replace( _T( "$linker" ), compiler->GetPrograms().LD );
  macro.Replace( _T( "$lib_linker" ), compiler->GetPrograms().LIB );
  macro.Replace( _T( "$options" ), cFlags );
  macro.Replace( _T( "$link_options" ), m_LDFlags[target] );
  macro.Replace( _T( "$includes" ), tmpIncludes );
  macro.Replace( _T( "$libdirs" ), m_Lib[target] );
  macro.Replace( _T( "$libs" ), m_LDAdd[target] );
  macro.Replace( _T( "$file_basename" ), tmpFname.GetName() );
  macro.Replace( _T( "$file_name" ), tmpFname.GetName() );
  macro.Replace( _T( "$file_dir" ), tmpFname.GetPath() );
  macro.Replace( _T( "$file_ext" ), tmpFname.GetExt() );
  macro.Replace( _T( "$file" ), tmpFile );
  macro.Replace( _T( "$dep_object" ), tmpDeps );
  if( target ) {
    tmp = target->GetObjectOutput();
    FixPathSeparators( compiler, tmp );
    macro.Replace( _T( "$objects_output_dir" ), tmp );
  }
  macro.Replace( _T( "$object" ), tmpObject );
  if( !target ) {
    wxFileName fname( UnquoteStringIfNeeded( object ) );
    fname.SetExt( FileFilters::EXECUTABLE_EXT );
    wxString output = fname.GetFullPath();
    QuoteStringIfNeeded( output );
    FixPathSeparators( compiler, output );
    macro.Replace( _T( "$exe_output" ), output );
    tmpOutFname.Assign( output );
  } else {
    macro.Replace( _T( "$exe_output" ), m_Output[target] );
    tmpOutFname.Assign( m_Output[target] );
  }
  macro.Replace( _T( "$exe_name" ), tmpOutFname.GetName() );
  macro.Replace( _T( "$exe_dir" ), tmpOutFname.GetPath() );
  macro.Replace( _T( "$exe_ext" ), tmpOutFname.GetExt() );
  macro.Replace( _T( "$link_objects" ), tmpObject );
  macro.Replace( _T( "$link_flat_objects" ), tmpFlatObject );
  macro.Replace( _T( "$+link_objects" ), tmpObject );
  macro.Replace( _T( "$-link_objects" ), tmpObject );
  macro.Replace( _T( "$-+link_objects" ), tmpObject );
  macro.Replace( _T( "$+-link_objects" ), tmpObject );
  macro.Replace( _T( "$all_link_objects_quoted" ), allObjectsQuoted );
  if( target && ( ( target->GetTargetType() == ttStaticLib ) || ( target->GetTargetType() == ttDynamicLib ) ) ) {
    if( ( target->GetTargetType() == ttStaticLib ) || ( target->GetCreateStaticLib() ) ) {
      macro.Replace( _T( "$static_output" ), m_StaticOutput[target] );
    } else {
      macro.Replace( _T( "-Wl,--out-implib=$static_output" ), _T( "" ) );
      macro.Replace( _T( "$static_output" ), _T( "" ) );
    }
    if( target->GetCreateDefFile() ) {
      macro.Replace( _T( "$def_output" ), m_DefOutput[target] );
    } else {
      macro.Replace( _T( "-Wl,--output-def=$def_output" ), _T( "" ) );
      macro.Replace( _T( "$def_output" ), _T( "" ) );
    }
  }
  Manager::Get()->GetMacrosManager()->ReplaceMacros( macro, target );
}

void CompilerCommandGenerator::DoBuildScripts( M_Code_Project* project, CompileTargetBase* target, const wxString& funcName ) {
  ProjectBuildTarget* bt = dynamic_cast<ProjectBuildTarget*>( target );
  static const wxString clearout_buildscripts = _T( "SetBuildOptions <- null;" );
  const wxArrayString& scripts = target->GetBuildScripts();
  for( size_t i = 0; i < scripts.GetCount(); ++i ) {
    wxString script_nomacro = scripts[i];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( script_nomacro, bt );
    script_nomacro = wxFileName( script_nomacro ).IsAbsolute() ? script_nomacro : project->GetBasePath() + wxFILE_SEP_PATH + script_nomacro;
    if( m_NotLoadedScripts.Index( script_nomacro ) != wxNOT_FOUND || m_ScriptsWithErrors.Index( script_nomacro ) != wxNOT_FOUND ) {
      continue;
    }
    Manager::Get()->GetScriptingManager()->LoadBuffer( clearout_buildscripts );
    if( !Manager::Get()->GetScriptingManager()->LoadScript( script_nomacro ) ) {
      m_NotLoadedScripts.Add( script_nomacro );
      continue;
    }
    try {
      SqPlus::SquirrelFunction<void> f( cbU2C( funcName ) );
      f( target );
    } catch( SquirrelError& e ) {
      Manager::Get()->GetScriptingManager()->DisplayErrors( &e );
      m_ScriptsWithErrors.Add( script_nomacro );
    }
  }
}

void CompilerCommandGenerator::FixPathSeparators( Compiler* compiler, wxString& inAndOut ) {
  if( !compiler || !compiler->GetSwitches().forceFwdSlashes ) {
    return;
  }
  size_t i = 0;
  while( i < inAndOut.Length() ) {
    if( inAndOut.GetChar( i ) == _T( '\\' ) &&
        ( i == inAndOut.Length() - 1 || inAndOut.GetChar( i + 1 ) != _T( ' ' ) ) ) {
      inAndOut.SetChar( i, _T( '/' ) );
    }
    ++i;
  }
}


wxString CompilerCommandGenerator::SetupOutputFilenames( Compiler* compiler, ProjectBuildTarget* target ) {
  if( !target ) {
    return wxEmptyString;
  }
  wxString result = target->GetOutputFilename();
  QuoteStringIfNeeded( result );
  FixPathSeparators( compiler, result );
  m_Output[target] = result;
  switch( target->GetTargetType() ) {
    case ttDynamicLib: {
      TargetFilenameGenerationPolicy PrefixPolicy;
      TargetFilenameGenerationPolicy ExtensionPolicy;
      target->GetTargetFilenameGenerationPolicy( PrefixPolicy, ExtensionPolicy );
      wxString importLibraryFileNameString( target->GetDynamicLibImportFilename() );
      Manager::Get()->GetMacrosManager()->ReplaceMacros( importLibraryFileNameString, target );
      wxFileName importLibraryFileName( UnquoteStringIfNeeded( importLibraryFileNameString ) );
      if( ( PrefixPolicy == tgfpPlatformDefault )
          && !importLibraryFileName.GetName().StartsWith( compiler->GetSwitches().libPrefix ) ) {
        importLibraryFileName.SetName( compiler->GetSwitches().libPrefix + importLibraryFileName.GetName() );
      }
      if( ExtensionPolicy == tgfpPlatformDefault ) {
        wxString current_ext   = importLibraryFileName.GetExt();
        wxString requested_ext = compiler->GetSwitches().libExtension;
        if( !current_ext.IsSameAs( requested_ext, false ) ) {
          importLibraryFileName.SetFullName( importLibraryFileName.GetFullName() + wxFILE_SEP_EXT + requested_ext );
        }
      }
      result = UnixFilename( importLibraryFileName.GetFullPath() );
      QuoteStringIfNeeded( result );
      FixPathSeparators( compiler, result );
      m_StaticOutput[target] = result;
      wxString definitionFileFileNameString( target->GetDynamicLibDefFilename() );
      Manager::Get()->GetMacrosManager()->ReplaceMacros( definitionFileFileNameString, target );
      wxFileName definitionFileFileName( UnquoteStringIfNeeded( definitionFileFileNameString ) );
      if( ( PrefixPolicy == tgfpPlatformDefault )
          && !definitionFileFileName.GetName().StartsWith( compiler->GetSwitches().libPrefix ) ) {
        definitionFileFileName.SetName( compiler->GetSwitches().libPrefix + definitionFileFileName.GetName() );
      }
      if( ExtensionPolicy == tgfpPlatformDefault ) {
        wxString current_ext   = definitionFileFileName.GetExt();
        wxString requested_ext = _T( "def" );
        if( !current_ext.IsSameAs( requested_ext, false ) ) {
          definitionFileFileName.SetFullName( definitionFileFileName.GetFullName() + wxFILE_SEP_EXT + requested_ext );
        }
      }
      result = UnixFilename( definitionFileFileName.GetFullPath() );
      QuoteStringIfNeeded( result );
      FixPathSeparators( compiler, result );
    }
    break;
    case ttExecutable:
    case ttConsoleOnly:
    case ttStaticLib:
    case ttCommandsOnly:
    case ttNative:
    default: {
      wxString fnameString( target->GetOutputFilename() );
      Manager::Get()->GetMacrosManager()->ReplaceMacros( fnameString, target );
      wxFileName fname( UnquoteStringIfNeeded( fnameString ) );
      TargetFilenameGenerationPolicy PrefixPolicy;
      TargetFilenameGenerationPolicy ExtensionPolicy;
      target->GetTargetFilenameGenerationPolicy( PrefixPolicy, ExtensionPolicy );
      if( ( PrefixPolicy == tgfpPlatformDefault )
          || ( target->GetTargetType() == ttDynamicLib ) ) {
        if( !fname.GetName().StartsWith( compiler->GetSwitches().libPrefix ) ) {
          fname.SetName( compiler->GetSwitches().libPrefix + fname.GetName() );
        }
      }
      if( ( ExtensionPolicy == tgfpPlatformDefault )
          || ( target->GetTargetType() == ttDynamicLib ) ) {
        wxString current_ext   = fname.GetExt();
        wxString requested_ext = compiler->GetSwitches().libExtension;
        if( ( false && !current_ext.IsSameAs( requested_ext, false ) )
            || ( !current_ext.IsSameAs( requested_ext ) ) ) {
          fname.SetFullName( fname.GetFullName() + wxFILE_SEP_EXT + requested_ext );
        }
      }
      result = UnixFilename( fname.GetFullPath() );
      QuoteStringIfNeeded( result );
      FixPathSeparators( compiler, result );
      m_StaticOutput[target] = result;
      fname.SetExt( _T( "def" ) );
      result = UnixFilename( fname.GetFullPath() );
      QuoteStringIfNeeded( result );
      FixPathSeparators( compiler, result );
    }
    break;
  }
  return result;
}

wxArrayString CompilerCommandGenerator::GetOrderedIncludeDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  wxArrayString result;
  if( target ) {
    wxArrayString prjSearchDirs = target->GetParentProject()->GetIncludeDirs();
    wxArrayString tgtSearchDirs = target->GetIncludeDirs();
    wxArrayString searchDirs;
    searchDirs = GetOrderedOptions( target, ortIncludeDirs, prjSearchDirs, tgtSearchDirs );
    for( unsigned int x = 0; x < searchDirs.GetCount(); ++x ) {
      Manager::Get()->GetMacrosManager()->ReplaceMacros( searchDirs[x], target );
    }
    if( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_prj_cwd" ), false ) ) {
      searchDirs.Add( target->GetParentProject()->GetBasePath() );
    }
    if( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_file_cwd" ), false ) ) {
      searchDirs.Add( _T( "." ) );
    }
    m_CompilerSearchDirs.insert( m_CompilerSearchDirs.end(), std::make_pair( target, searchDirs ) );
    result = GetOrderedOptions( target, ortIncludeDirs, target->GetParentProject()->GetIncludeDirs(), target->GetIncludeDirs() );
  }
  const wxArrayString& carr = compiler->GetIncludeDirs();
  for( unsigned int x = 0; x < carr.GetCount(); ++x ) {
    result.Add( carr[x] );
  }
  for( unsigned int x = 0; x < result.GetCount(); ++x ) {
    wxString& tmp( result[x] );
    Manager::Get()->GetMacrosManager()->ReplaceMacros( tmp, target );
    FixPathSeparators( compiler, tmp );
    if( tmp.Trim().IsEmpty() ) {
      Manager::Get()->GetLogManager()->DebugLogError( _T( "Warning: Compiler include folder evaluates to empty value." ) );
    }
  }
  return result;
}

wxArrayString CompilerCommandGenerator::GetOrderedLibrariesDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  wxArrayString result;
  if( target ) {
    wxArrayString prjSearchDirs = target->GetParentProject()->GetLibDirs();
    wxArrayString tgtSearchDirs = target->GetLibDirs();
    wxArrayString searchDirs;
    searchDirs = GetOrderedOptions( target, ortLibDirs, prjSearchDirs, tgtSearchDirs );
    for( unsigned int x = 0; x < searchDirs.GetCount(); ++x ) {
      Manager::Get()->GetMacrosManager()->ReplaceMacros( searchDirs[x], target );
      wxFileName fn( UnquoteStringIfNeeded( searchDirs[x] ) );
      if( fn.IsRelative() ) {
        fn.MakeAbsolute( target->GetParentProject()->GetBasePath() );
        searchDirs[x] = fn.GetFullPath();
      }
    }
    m_LinkerSearchDirs.insert( m_LinkerSearchDirs.end(), std::make_pair( target, searchDirs ) );
    result = GetOrderedOptions( target, ortLibDirs, target->GetParentProject()->GetLibDirs(), target->GetLibDirs() );
  }
  const wxArrayString& carr = compiler->GetLibDirs();
  for( unsigned int x = 0; x < carr.GetCount(); ++x ) {
    result.Add( carr[x] );
  }
  for( unsigned int x = 0; x < result.GetCount(); ++x ) {
    wxString& tmp( result[x] );
    Manager::Get()->GetMacrosManager()->ReplaceMacros( tmp, target );
    FixPathSeparators( compiler, tmp );
    if( tmp.Trim().IsEmpty() ) {
      Manager::Get()->GetLogManager()->DebugLogError( _T( "Warning: Linker include folder evaluates to empty value." ) );
    }
  }
  return result;
}

wxArrayString CompilerCommandGenerator::GetOrderedResourceIncludeDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  wxArrayString result;
  if( target ) {
    result = GetOrderedOptions( target, ortResDirs, target->GetParentProject()->GetResourceIncludeDirs(), target->GetResourceIncludeDirs() );
  }
  const wxArrayString& carr = compiler->GetResourceIncludeDirs();
  for( unsigned int x = 0; x < carr.GetCount(); ++x ) {
    result.Add( carr[x] );
  }
  for( unsigned int x = 0; x < result.GetCount(); ++x ) {
    wxString& tmp( result[x] );
    Manager::Get()->GetMacrosManager()->ReplaceMacros( tmp, target );
    FixPathSeparators( compiler, tmp );
    if( tmp.Trim().IsEmpty() ) {
      Manager::Get()->GetLogManager()->DebugLogError( _T( "Warning: Resource compiler include folder evaluates to empty value." ) );
    }
  }
  return result;
}

wxString CompilerCommandGenerator::MakeOptString( const wxArrayString& arr, const wxString& opt, wxChar separator ) {
  wxString result;
  bool subseq( false );
  if( opt.EndsWith( _T( "(" ) ) ) {
    result << opt;
    for( unsigned int x = 0; x < arr.GetCount(); ++x ) {
      if( subseq ) {
        result << separator;
      }
      subseq = true;
      wxString tmp( arr[x] );
      QuoteStringIfNeeded( tmp );
      result << tmp;
    }
    result << _T( ')' );
    return result;
  }
  for( unsigned int x = 0; x < arr.GetCount(); ++x ) {
    if( subseq ) {
      result << separator;
    }
    subseq = true;
    wxString tmp( arr[x] );
    QuoteStringIfNeeded( tmp );
    result << opt << tmp;
  }
  return result;
}

wxString CompilerCommandGenerator::PathSearch( const wxArrayString& arr, const wxString& filename ) {
  Manager::Get()->GetLogManager()->Log( _T( "PathSearch: " ) + filename );
  if( wxFileExists( filename ) ) {
    return filename;
  }
  for( unsigned int x = 0; x < arr.GetCount(); ++x ) {
    wxString fn( arr[x] + wxFILE_SEP_PATH + filename );
    Manager::Get()->GetLogManager()->Log( _T( "PathSearch: trying: " ) + fn );
    if( wxFileExists( fn ) ) {
      return fn;
    }
  }
  Manager::Get()->GetLogManager()->Log( _T( "PathSearch: end: " ) + filename );
  return filename;
}


wxString CompilerCommandGenerator::SetupIncludeDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  return MakeOptString( GetOrderedIncludeDirs( compiler, target ),
                        compiler->GetSwitches().includeDirs,
                        compiler->GetSwitches().includeDirSeparator );
}


wxString CompilerCommandGenerator::SetupLibrariesDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  if( compiler->GetSwitches().linkerNeedsPathResolved ) {
    return wxString();
  }
  return MakeOptString( GetOrderedLibrariesDirs( compiler, target ),
                        compiler->GetSwitches().libDirs,
                        compiler->GetSwitches().libDirSeparator );
}


wxString CompilerCommandGenerator::SetupResourceIncludeDirs( Compiler* compiler, ProjectBuildTarget* target ) {
  return MakeOptString( GetOrderedResourceIncludeDirs( compiler, target ),
                        compiler->GetSwitches().includeDirs,
                        compiler->GetSwitches().includeDirSeparator );
}


wxString CompilerCommandGenerator::SetupCompilerOptions( Compiler* compiler, ProjectBuildTarget* target ) {
  wxString result;
  if( target ) {
    wxString tstr = GetStringFromArray( target->GetCompilerOptions(), _T( ' ' ) ) << _T( " " );
    wxString pstr = GetStringFromArray( target->GetParentProject()->GetCompilerOptions(), _T( ' ' ) ) << _T( " " );
    result = GetOrderedOptions( target, ortCompilerOptions, pstr, tstr );
  }
  result << GetStringFromArray( compiler->GetCompilerOptions(), _T( ' ' ) ) << _T( " " );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( result, target );
  wxString bt = ExpandBackticks( result );
  SearchDirsFromBackticks( compiler, target, bt );
  return result;
}


wxString CompilerCommandGenerator::SetupLinkerOptions( Compiler* compiler, ProjectBuildTarget* target ) {
  wxString result;
  if( target ) {
    wxString tstr = GetStringFromArray( target->GetLinkerOptions(), _T( ' ' ) );
    wxString pstr = GetStringFromArray( target->GetParentProject()->GetLinkerOptions(), _T( ' ' ) );
    result = GetOrderedOptions( target, ortLinkerOptions, pstr, tstr );
  }
  result << GetStringFromArray( compiler->GetLinkerOptions(), _T( ' ' ) );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( result, target );
  wxString bt = ExpandBackticks( result );
  SearchDirsFromBackticks( compiler, target, bt );
  return result;
}

wxString CompilerCommandGenerator::FixupLinkLibraries( Compiler* compiler, const wxString& lib ) {
  if( lib.IsEmpty() ) {
    return wxEmptyString;
  }
  wxString result = lib;
  wxString libPrefix = compiler->GetSwitches().libPrefix;
  wxString libExt = compiler->GetSwitches().libExtension;
  QuoteStringIfNeeded( result );
  FixPathSeparators( compiler, result );
  if( result.Find( '/' ) == -1 && result.Find( '\\' ) == -1 ) {
    bool hadLibPrefix = false;
    if( !compiler->GetSwitches().linkerNeedsLibPrefix &&
        !libPrefix.IsEmpty() &&
        result.StartsWith( libPrefix ) ) {
      result.Remove( 0, libPrefix.Length() );
      hadLibPrefix = true;
    }
    if( !compiler->GetSwitches().linkerNeedsLibExtension &&
        result.Length() > libExt.Length() &&
        result.Right( libExt.Length() + 1 ) == _T( "." ) + libExt ) {
      if( hadLibPrefix ) {
        result.RemoveLast( libExt.Length() + 1 );
      }
    } else if( compiler->GetSwitches().linkerNeedsLibExtension &&
               !libExt.IsEmpty() ) {
      if( result.Length() <= libExt.Length() ||
          result.Right( libExt.Length() + 1 ) != _T( "." ) + libExt ) {
        result << _T( "." ) << libExt;
      }
    }
    result = compiler->GetSwitches().linkLibs + result;
  }
  return result;
}


wxString CompilerCommandGenerator::SetupLinkLibraries( Compiler* compiler, ProjectBuildTarget* target ) { //??????
  wxArrayString libs;
  if( target ) {
    libs = GetOrderedOptions( target, ortLinkerOptions, target->GetParentProject()->GetLinkLibs(), target->GetLinkLibs() );
  }
  const wxArrayString& carr = compiler->GetLinkLibs();
  for( unsigned int x = 0; x < carr.GetCount(); ++x ) {
    libs.Add( carr[x] );
  }
  for( unsigned int x = 0; x < libs.GetCount(); ++x ) {
    libs[x] = FixupLinkLibraries( compiler, libs[x] );
  }
  if( compiler->GetSwitches().linkerNeedsPathResolved ) {
    wxArrayString path( GetOrderedLibrariesDirs( compiler, target ) );
    for( unsigned int x = 0; x < libs.GetCount(); ++x ) {
      libs[x] = PathSearch( path, libs[x] );
    }
  }
  wxString result;
  bool subseq( false );
  for( unsigned int x = 0; x < libs.GetCount(); ++x ) {
    if( subseq ) {
      result << compiler->GetSwitches().objectSeparator;
    }
    subseq = true;
    wxString tmp( libs[x] );
    QuoteStringIfNeeded( tmp );
    result << tmp;
  }
  return result;
}


wxString CompilerCommandGenerator::SetupResourceCompilerOptions( M_Code_unused Compiler* compiler, M_Code_unused ProjectBuildTarget* target ) {
  wxString result;
  if( target ) {
    wxString tstr = GetStringFromArray( target->GetResourceCompilerOptions(), _T( ' ' ) );
    wxString pstr = GetStringFromArray( target->GetParentProject()->GetResourceCompilerOptions(), _T( ' ' ) );
    result = GetOrderedOptions( target, ortCompilerOptions, pstr, tstr );
  }
  result << GetStringFromArray( compiler->GetResourceCompilerOptions(), _T( ' ' ) );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( result, target );
  wxString bt = ExpandBackticks( result );
  SearchDirsFromBackticks( compiler, target, bt );
  return result;
}

const wxArrayString& CompilerCommandGenerator::GetCompilerSearchDirs( ProjectBuildTarget* target ) {
  static wxArrayString retIfError;
  retIfError.Clear();
  SearchDirsMap::iterator it = m_CompilerSearchDirs.find( target );
  if( it == m_CompilerSearchDirs.end() ) {
    return retIfError;
  }
  return it->second;
}

const wxArrayString& CompilerCommandGenerator::GetLinkerSearchDirs( ProjectBuildTarget* target ) {
  static wxArrayString retIfError;
  retIfError.Clear();
  SearchDirsMap::iterator it = m_LinkerSearchDirs.find( target );
  if( it == m_LinkerSearchDirs.end() ) {
    return retIfError;
  }
  return it->second;
}


wxString CompilerCommandGenerator::GetOrderedOptions( const ProjectBuildTarget* target, OptionsRelationType rel, const wxString& project_options, const wxString& target_options ) {
  wxString result;
  OptionsRelation relation = target->GetOptionRelation( rel );
  switch( relation ) {
    case orUseParentOptionsOnly:
      result << project_options;
      break;
    case orUseTargetOptionsOnly:
      result << target_options;
      break;
    case orPrependToParentOptions:
      result << target_options << project_options;
      break;
    case orAppendToParentOptions:
      result << project_options << target_options;
      break;
    default:
      break;
  }
  return result;
}


wxArrayString CompilerCommandGenerator::GetOrderedOptions( const ProjectBuildTarget* target, OptionsRelationType rel, const wxArrayString& project_options, const wxArrayString& target_options ) {
  wxArrayString result;
  OptionsRelation relation = target->GetOptionRelation( rel );
  switch( relation ) {
    case orUseParentOptionsOnly:
      for( size_t i = 0; i < project_options.GetCount(); ++i ) {
        result.Add( project_options[i] );
      }
      break;
    case orUseTargetOptionsOnly:
      for( size_t i = 0; i < target_options.GetCount(); ++i ) {
        result.Add( target_options[i] );
      }
      break;
    case orPrependToParentOptions:
      for( size_t i = 0; i < target_options.GetCount(); ++i ) {
        result.Add( target_options[i] );
      }
      for( size_t i = 0; i < project_options.GetCount(); ++i ) {
        result.Add( project_options[i] );
      }
      break;
    case orAppendToParentOptions:
      for( size_t i = 0; i < project_options.GetCount(); ++i ) {
        result.Add( project_options[i] );
      }
      for( size_t i = 0; i < target_options.GetCount(); ++i ) {
        result.Add( target_options[i] );
      }
      break;
    default:
      break;
  }
  return result;
}


wxString CompilerCommandGenerator::GetProcessedIncludeDir( Compiler* compiler, ProjectBuildTarget* target,
    const wxArrayString& inc_dirs, const wxString& inc_switch ) {
  wxString inc_string;
  for( size_t x = 0; x < inc_dirs.GetCount(); ++x ) {
    wxString inc_dir = inc_dirs[x];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( inc_dir, target );
    QuoteStringIfNeeded( inc_dir );
    FixPathSeparators( compiler, inc_dir );
    inc_string << inc_switch << inc_dir << _T( ' ' );
  }
  return inc_string;
}



void CompilerCommandGenerator::SearchDirsFromBackticks( Compiler* compiler, ProjectBuildTarget* target, const wxString& btOutput ) {
  if( btOutput.IsEmpty() ) {
    return;
  }
  size_t pos = 0;
  while( true ) {
    pos = btOutput.find( compiler->GetSwitches().includeDirs, pos );
    if( pos == wxString::npos ) {
      break;
    }
    pos += compiler->GetSwitches().includeDirs.Length();
    size_t pos2 = btOutput.find( _T( ' ' ), pos );
    if( pos2 != pos ) {
      if( pos2 == wxString::npos ) {
        m_CompilerSearchDirs[target].Add( btOutput.Mid( pos, btOutput.Length() - pos ) );
      } else
      { m_CompilerSearchDirs[target].Add( btOutput.Mid( pos, pos2 - pos ) ); }
    }
    ++pos;
  }
  pos = 0;
  while( true ) {
    pos = btOutput.find( compiler->GetSwitches().libDirs, pos );
    if( pos == wxString::npos ) {
      break;
    }
    pos += compiler->GetSwitches().libDirs.Length();
    size_t pos2 = btOutput.find( _T( ' ' ), pos );
    if( pos2 != pos ) {
      if( pos2 == wxString::npos ) {
        m_LinkerSearchDirs[target].Add( btOutput.Mid( pos, btOutput.Length() - pos ) );
      } else
      { m_LinkerSearchDirs[target].Add( btOutput.Mid( pos, pos2 - pos ) ); }
    }
    ++pos;
  }
}
